/*
 * Copyright (C) 2007, 2013, 2016 XStream Committers.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 * 
 * Created on 01. February 2007 by Joerg Schaible
 */
package com.feilong.lib.xstream.converters.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.AttributedCharacterIterator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.feilong.lib.xstream.converters.ConversionException;
import com.feilong.lib.xstream.converters.basic.AbstractSingleValueConverter;
import com.feilong.lib.xstream.core.util.Fields;

/**
 * An abstract converter implementation for constants of
 * {@link java.text.AttributedCharacterIterator.Attribute} and derived types.
 * 
 * @author J&ouml;rg Schaible
 * @since 1.2.2
 */
public class AbstractAttributedCharacterIteratorAttributeConverter extends AbstractSingleValueConverter{

    private static final Map    instanceMaps = new HashMap();

    private static final Method getName;
    static{
        Method method = null;
        try{
            method = AttributedCharacterIterator.Attribute.class.getDeclaredMethod("getName", (Class[]) null);
            if (!method.isAccessible()){
                method.setAccessible(true);
            }
        }catch (SecurityException e){
            // ignore for now
        }catch (NoSuchMethodException e){
            // ignore for now
        }
        getName = method;
    }

    private final Class   type;

    private transient Map attributeMap;

    public AbstractAttributedCharacterIteratorAttributeConverter(final Class type){
        super();
        if (!AttributedCharacterIterator.Attribute.class.isAssignableFrom(type)){
            throw new IllegalArgumentException(type.getName() + " is not a " + AttributedCharacterIterator.Attribute.class.getName());
        }
        this.type = type;
        readResolve();
    }

    @Override
    public boolean canConvert(final Class type){
        return type == this.type && !attributeMap.isEmpty();
    }

    @Override
    public String toString(final Object source){
        return getName((AttributedCharacterIterator.Attribute) source);
    }

    private String getName(AttributedCharacterIterator.Attribute attribute){
        Exception ex = null;
        if (getName != null){
            try{
                return (String) getName.invoke(attribute, (Object[]) null);
            }catch (IllegalAccessException e){
                ex = e;
            }catch (InvocationTargetException e){
                ex = e;
            }
        }
        String s = attribute.toString();
        String className = attribute.getClass().getName();
        if (s.startsWith(className)){
            return s.substring(className.length() + 1, s.length() - 1);
        }
        ConversionException exception = new ConversionException("Cannot find name of attribute", ex);
        exception.add("attribute-type", className);
        throw exception;
    }

    @Override
    public Object fromString(final String str){
        if (attributeMap.containsKey(str)){
            return attributeMap.get(str);
        }
        ConversionException exception = new ConversionException("Cannot find attribute");
        exception.add("attribute-type", type.getName());
        exception.add("attribute-name", str);
        throw exception;
    }

    private Object readResolve(){
        attributeMap = (Map) instanceMaps.get(type.getName());
        if (attributeMap == null){
            attributeMap = new HashMap();
            Field instanceMap = Fields.locate(type, Map.class, true);
            if (instanceMap != null){
                try{
                    Map map = (Map) Fields.read(instanceMap, null);
                    if (map != null){
                        boolean valid = true;
                        for (Iterator iter = map.entrySet().iterator(); valid && iter.hasNext();){
                            Map.Entry entry = (Map.Entry) iter.next();
                            valid = entry.getKey().getClass() == String.class && entry.getValue().getClass() == type;
                        }
                        if (valid){
                            attributeMap.putAll(map);
                        }
                    }
                }catch (ObjectAccessException e){}
            }
            if (attributeMap.isEmpty()){
                try{
                    Field[] fields = type.getDeclaredFields();
                    for (Field field : fields){
                        if (field.getType() == type == Modifier.isStatic(field.getModifiers())){
                            AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) Fields
                                            .read(field, null);
                            attributeMap.put(toString(attribute), attribute);
                        }
                    }
                }catch (SecurityException e){
                    attributeMap.clear();
                }catch (ObjectAccessException e){
                    attributeMap.clear();
                }catch (NoClassDefFoundError e){
                    attributeMap.clear();
                }
            }
            instanceMaps.put(type.getName(), attributeMap);
        }
        return this;
    }

}
